package com.maimao.user.service.sys;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.maimao.core.constant.Constants;
import com.maimao.core.enums.RespCode;
import com.maimao.core.exception.MaiMaoDaoException;
import com.maimao.core.model.resp.PageResult;
import com.maimao.core.model.resp.Result;
import com.maimao.model.user.sys.Resources;
import com.maimao.model.user.sys.dto.DelDto;
import com.maimao.model.user.sys.dto.ResourceSearch;
import com.maimao.model.user.sys.vo.ResourcesEx;
import com.maimao.user.mapper.sys.ResourcesMapper;
import org.apache.commons.configuration.tree.TreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.maimao.core.constant.Constants.ONE;

/**
 * @author MaoLin Wang
 * @date 2020/12/2 4:29 下午
 */
@Service
public class ResourceService {
    @Autowired
    private ResourcesMapper resourcesMapper;

    /**
     * 获取用户资源列表
     *
     * @param userId
     * @return
     */
    public List<Resources> resourceListByUserId(Integer userId) {
        /**
         * 查询当前用户所有权限
         */
        List<Resources> resources = resourcesMapper.selectParentByUserId(userId);
        //需要获取已有资源到根节点的资源树
        return groupByParent(resources);
    }

    /**
     * 对resources进行分组，算法： 1.{code:groupByParent}对List按pid进行分组，对于每组资源，如果当前List中存在其父级，则将该组资源设为其父级的孩子；
     * 2、对进行1操作后的所有集合，去掉已经设置了父级的资源，即将1中所有找到父级的资源从原集合中移除掉，因为其已经在某个父级的children中； 3、如果2操作后的集合只有一个，且父id为0，即只有一个顶级资源，则直接返回；
     * 4、如果3不满足，则对2操作后的集合再根据pid进行分组 {code: groupByParent0}，找到每组的父级，然后设置到父级的children中； 5、4操作又得到了一组需要进行1操作的集合，递归调用1
     *
     * @param resources 待分组资源
     */
    private List<Resources> groupByParent(List<Resources> resources) {
        if (ObjectUtil.isNotEmpty(resources)) {
            Map<Integer, Resources> idMap = resources.stream().collect(Collectors.toMap(Resources::getId, r -> r));
            Map<Integer, List<Resources>> pidMap = resources.stream().collect(Collectors.groupingBy(Resources::getParentId));

            List<Resources> removed = Lists.newArrayListWithCapacity(idMap.size());
            for (Map.Entry<Integer, List<Resources>> entry : pidMap.entrySet()) {
                Integer pid = entry.getKey();
                Resources parent = idMap.get(pid);
                if (!ObjectUtil.isNull(parent)) {
                    //将pid在已有数据的集合设置到父级的孩子中，然后从原集合remove掉
                    if (ObjectUtil.isNotEmpty(parent.getChildren())) {
                        parent.getChildren().addAll(entry.getValue());
                    } else {
                        parent.setChildren(entry.getValue());
                    }
                    removed.addAll(entry.getValue());
                }
                   /* //已有数据中没有该数据的父级，当前pid下的所有数据单独作为一组
//                    tree.add(entry.getValue());
                } else {
                    //已有数据有该数据的父级，则将当前pid下的数据设置为该父级的孩子
                    tree.add(CollUtil.newArrayList(parent));
                }*/
            }
            List<Resources> values = Lists.newArrayList(idMap.values());
            //去掉已经设置到父级的孩子
            values.removeAll(removed);
            if (values != null && values.size() == 1 && values.get(0).getParentId().equals(Constants.ZERO)) {
                //已经找到最顶级
                return values;
            }
            pidMap = values.stream().collect(Collectors.groupingBy(Resources::getParentId));
            return groupByParent0(pidMap);
        }
        return Collections.emptyList();
    }

    /**
     * 对groupByParent中分组后的资源，查询父级后递归调用groupByParent
     *
     * @param pidMap pid->父级为pid的资源集合
     */
    private List<Resources> groupByParent0(Map<Integer, List<Resources>> pidMap) {
        List<Resources> list = Lists.newArrayListWithCapacity(pidMap.size());
        for (Map.Entry<Integer, List<Resources>> entry : pidMap.entrySet()) {
            Integer pid = entry.getKey();
            Resources parent = resourcesMapper.selectByIdValid(pid);
            if (parent==null){
                continue;
            }
            parent.setChildren(entry.getValue());
            list.add(parent);
        }
        return groupByParent(list);
    }

    public List<Resources> treeData(Integer all) {
        if (all != null && all.equals(ONE)) {
            return resourcesMapper.selectTreeAll(0);
        }
        return resourcesMapper.selectTree(0);

    }

    public List<String> searchByNameLike(String key) {
        return resourcesMapper.selectByNameLike(key);
    }

    public PageResult<ResourcesEx> selectChild1(Integer parentId, Integer page, Integer size, ResourceSearch search) {
        PageHelper.startPage(page, size);

        List<ResourcesEx> resourcesExes = resourcesMapper.selectByParentId(parentId, search.getName(), search.getStatus());
        return new PageResult<>(resourcesExes);
    }

    /**
     * @param resources
     * @return
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result<Void> add(Resources resources) {
        Integer parentId = resources.getParentId();
        Integer type = resources.getType();
        Resources parent = resourcesMapper.selectByIdValid(parentId);
        Result res = doCheckResources(resources, type, parent);
        if (res != null) {
            return res;
        }

        resourcesMapper.insert(resources);
        resourcesMapper.insertRoleResourceRecord(resources.getId(), Constants.SUPER_ADMIN);

        return Result.ok(RespCode.OK, "添加成功");
    }

    private Result doCheckResources(Resources resources, Integer type, Resources parent) {
        if (parent == null) {
            return Result.fail(RespCode.FAIL, "父资源已不使用，请重新选择");
        }
        if (type.equals(Constants.LEFT_NAV)) {
            if (!parent.getParentId().equals(Constants.MENU_BASE)) {
                return Result.notValid("左侧导航目前只能作为顶部菜单的直接子菜单");
            }
        } else if (type.equals(Constants.MENU)) {
            if (!parent.getType().equals(Constants.MENU) && !parent.getType().equals(Constants.LEFT_NAV)) {
                return Result.notValid("菜单不可作为按钮或读写权限的子资源");

            }
            if (resources.getSide()) {
                if (parent.getParentId().equals(Constants.MENU_BASE)) {
                    return Result.notValid("左侧菜单需选择二级及二级以下资源作为父资源");
                }
            } else {
                if (!parent.getId().equals(Constants.MENU_BASE)) {
                    return Result.notValid("顶部菜单只能作为一级资源的子资源");
                }
            }
        }
        return null;
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result<Void> update(Resources resources) {

        Integer parentId = resources.getParentId();
        Integer type = resources.getType();
        Resources parent = resourcesMapper.selectByIdValid(parentId);
        Result res = doCheckResources(resources, type, parent);
        if (res != null) {
            return res;
        }
        resourcesMapper.updateById(resources);

        return Result.ok(RespCode.OK, "修改成功");

    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void updateStatusBatch(@Valid DelDto delDto) {
        //没更新操作人
        if (delDto.getStatus().equals(Constants.STATUS_1) || delDto.getStatus().equals(Constants.STATUS_0)) {
            //恢复
            resourcesMapper.updateStatusBatch(delDto.getIds(), delDto.getStatus());
            for (Integer id : delDto.getIds()) {
                List<Resources> resources = resourcesMapper.selectTree(id);
                if (ObjectUtil.isNotEmpty(resources)){
                    List<Integer>childIds=Lists.newArrayList();
                    for (Resources resource : resources) {
                        childIds.add(resource.getId());
                        List<Resources> children = resource.getChildren();
                        if (ObjectUtil.isNotEmpty(children)){
                            addChild(childIds,children);
                        }
                    }
                    if (ObjectUtil.isNotEmpty(childIds)) {
                        resourcesMapper.updateStatusBatch(childIds,delDto.getStatus());
                    }
                }
            }
        }
    }

    private void addChild(List<Integer> childIds, List<Resources> children) {
        List<Integer> collect = children.stream().map(Resources::getId).collect(Collectors.toList());
        childIds.addAll(collect);
        for (Resources child : children) {
            if (ObjectUtil.isNotEmpty(child.getChildren())){
                addChild(childIds,children);
            }
        }
    }
}
